package com.hspedu.seckill.controller;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.hspedu.seckill.entity.Order;
import com.hspedu.seckill.entity.SeckillOrder;
import com.hspedu.seckill.entity.User;
import com.hspedu.seckill.model.annotation.AccessLimit;
import com.hspedu.seckill.model.annotation.WebLog;
import com.hspedu.seckill.model.constant.RedisCacheConstant;
import com.hspedu.seckill.model.constant.RespBeanEnum;
import com.hspedu.seckill.model.dto.SeckillMessage;
import com.hspedu.seckill.model.result.RespBean;
import com.hspedu.seckill.model.vo.GoodsVo;
import com.hspedu.seckill.rabbitmq.MQSenderMessage;
import com.hspedu.seckill.service.GoodsService;
import com.hspedu.seckill.service.OrderService;
import com.hspedu.seckill.service.SeckillOrderService;
import com.ramostear.captcha.HappyCaptcha;
import com.ramostear.captcha.common.Fonts;
import com.ramostear.captcha.support.CaptchaStyle;
import com.ramostear.captcha.support.CaptchaType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author: xujiabing
 * @date: 2024-10-05 17:41
 * @description 秒杀控制层
 */
@Controller
@Slf4j
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {
    @Autowired
    private GoodsService goodsService;
    @Autowired
    private SeckillOrderService seckillOrderService;
    @Autowired
    private OrderService orderService;
    @Resource
    private RedisTemplate redisTemplate;
    //装配RedisScript
    @Resource
    private RedisScript<Long> redisScript;
    @Resource
    private MQSenderMessage mqSenderMessage;

    /**
     * 内存标记, 记录秒杀商品是否还有库存
     */
    private Map<Long, Boolean> entryStockMap = new HashMap<>();

    @GetMapping("/captcha")
    public void captcha(HttpServletRequest req, HttpServletResponse res,
                        User user, Long goodsId) {

        // 生成验证码并返回, 默认保存到session中
        HappyCaptcha.require(req,res)
                .style(CaptchaStyle.ANIM)			//设置展现样式为动画
                .type(CaptchaType.NUMBER)			//设置验证码内容为汉字
                .length(6)							//设置字符长度为6
                .width(220)							//设置动画宽度为220
                .height(80)							//设置动画高度为80
                .font(Fonts.getInstance().zhFont())	//设置汉字的字体
                .build().finish();      			//生成并输出验证码

        //把验证码的值存放到 Redis 中, userId+goodsId,有效时间为100s
        String captchaCacheKey = orderService
                .genCaptchaCacheKey(user.getId().toString(), goodsId.toString());

        redisTemplate.opsForValue().set
                (captchaCacheKey, (String)
                        req.getSession().getAttribute("happy-captcha"), 100 , TimeUnit.SECONDS);
    }

    /**
     * @description: 获取秒杀需要的uuid + 通用限流防刷机制
     * @author: xujiabing
     * @date: 2025-01-27 20:26
     * @param: user
     * @param: goodsVo
     * @return: RespBean
     */
    @WebLog
    @RequestMapping("/path")
    @ResponseBody
    @AccessLimit(second = 5, maxCount = 5, needLogin = true)
    public RespBean getOnOrderUUID(User user, Long goodsId, String captcha, HttpServletRequest request) {
        if (Objects.isNull(user) || Objects.isNull(goodsId) || StringUtils.isEmpty(captcha)) {
            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
        }
        // v2.0 接口限流防刷机制-@AccessLimit+自定义拦截器替代

        // todo 增加校验秒杀验证码的逻辑，如果失败抛出异常即可
        boolean checked = orderService
                .checkCaptcha(user.getId().toString(), goodsId.toString(), captcha);

        if (!checked) {
            return RespBean.error(RespBeanEnum.CAPTCHA_ERROR);
        }

        String uuid = orderService.genOnOrderUUID(user, goodsId);
        return RespBean.success(uuid);
    }

    /**
     * @description: 检查订单秒杀状态
     * @author: xujiabing
     * @date: 2024-12-22 21:13
     * @param: user
     * @param: goodsId
     * @return: RespBean
     */
    @WebLog
    @ResponseBody
    @RequestMapping(value = "/checkSeckillStatus")
    public RespBean checkSeckillStatus(User user, Long goodsId) {
        if (Objects.isNull(user)) {// 用户没有登录
            return RespBean.error(RespBeanEnum.LOSS_LOGIN);
        }
        // 查看redis中是否有秒杀失败记录
        Object object = redisTemplate
                .opsForValue().get("seckillFail:" + user.getId() + ":" + goodsId);
        // 秒杀失败
        if (!Objects.isNull(object)) {
            return RespBean.success(-1);
        }
        // 查询数据库记录
        Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getUserId, user.getId())
                .eq(Order::getGoodsId, goodsId)
        );
        // 秒杀成功
        if (!Objects.isNull(order)) {
            return RespBean.success(order.getId());
        }
        // 返回排队中状态
        return RespBean.success(0);
    }

    /**
     * 处理秒杀商品下单v6.0
     * redis预减库存 + 内存标记(减少到redis中 预减库存 次数) + rabbitmq 消息队列 + 下单path(uuid)校验
     * @param model
     * @param user
     * @param goodsId
     * @return
     */
    @WebLog
    @RequestMapping(value = "{path}/doSeckill")
    @ResponseBody
    public RespBean doSeckill(@PathVariable String path, User user, Long goodsId) {
        if (Objects.isNull(user)) {// 用户没有登录
            return RespBean.error(RespBeanEnum.LOSS_LOGIN);
        }
        // v6.0 增加校验用户携带的, 下单uuid是否正确
        Boolean aBoolean = orderService.checkOnOrderUUID(user, goodsId, path);
        if (!aBoolean) {
            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
        }

        // 获取到goodsVo
        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
        // 判断库存
        if (goodsVo.getStockCount() < 1) {
            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
        }

        // v2.0判断用户是否复购[限购], 到redis中查询
        ValueOperations opsForValue = redisTemplate.opsForValue();
        SeckillOrder seckillOrder = (SeckillOrder)opsForValue
                .get("order:" + user.getId() + ":" + goodsVo.getId());

        if (!Objects.isNull(seckillOrder)) {
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }

        // v4.0 对map进行判断[内存标记], 如果商品在map中，已经标记为没有库存, 不再进行redis预减库存
        if (entryStockMap.get(goodsId)) {
            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
        }

        // v3.0增加 预减库存机制,如果在redis中预减库存, 发现商品已经没有了, 直接返回
        // 达到减少执行 seckill 方法, 防止线程堆积, 优化高并发秒杀
        // decrement 方法具有原子性
        // 注意: 不能将这段代码放到, 判断用户复购前面, 可能会导致库存剩余,
        // 库存预减 => 判断复购，如果恰好当前用户 已经购买, 直接返回信息, redis库存 白减1

        Long decrement = redisTemplate.opsForValue()
                .decrement(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
        // 此时当前商品已经没有库存
        if (decrement.intValue() < 0) {
            // v4.0 内存标记, 此时当前商品, 没有库存
            entryStockMap.put(goodsId, true);
            // 恢复库存数为 0
            redisTemplate.opsForValue().increment(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
        }

        // 抢购, 向mq 发送秒杀商品请求，实现异步秒杀请求
        // 这里发送秒杀消息后，马上返回临时结果,比如排队中,因为不知道是否成功, 客户端需要轮询
        // errmsg 为排队中.... , 暂时返回"secKillFail" 页面
        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
        mqSenderMessage.sendSeckillMessage(JSONUtil.toJsonStr(seckillMessage));

        return RespBean.error(RespBeanEnum.SEC_KILL_WAIT);
    }

    /**
     * 处理秒杀商品下单v6.0-拓展
     * redis预减库存 + 内存标记(减少到redis中 预减库存 次数) + rabbitmq 消息队列 + redis分布式锁(拓展需要)
     * 因为这里只有 decrement 操作,该方法本身具有原子性，所有不需要redis分布式锁
     * @param model
     * @param user
     * @param goodsId
     * @return
     */
    @WebLog
    @RequestMapping(value = "/doSeckill")
    public String doSeckill(Model model, User user, Long goodsId) {
        if (Objects.isNull(user)) {// 用户没有登录
            return "login";
        }

        //将user放入model中
        model.addAttribute("user",user);

        //获取到goodsVo
        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
        //判断库存
        if (goodsVo.getStockCount() < 1) {
            //错误信息
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";
        }

        //判断用户是否复购[限购]
        //v2.0 改成到redis中查询
        ValueOperations opsForValue = redisTemplate.opsForValue();
        SeckillOrder seckillOrder = (SeckillOrder)opsForValue
                .get("order:" + user.getId() + ":" + goodsVo.getId());

        if (!Objects.isNull(seckillOrder)) {
            //错误信息
            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
            return "secKillFail";
        }

        // v4.0 对map进行判断[内存标记], 如果商品在redis中，已经标记为没有库存, 不再进行redis 预减库存
        if (entryStockMap.get(goodsId)) {
            //错误信息
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";
        }

        //============使用Redis 分布式锁=========
        //1. 对于当前项目,使用redisTemplate.opsForValue().decrement()就可以控制
        //2. 考虑到如果有比较多的操作，需要保证隔离性, 和对Redis 操作也不是简单的-1，
        // 而是有多个操作, 我们给小伙伴讲解一下如何使用redis 分布式锁来控制

        //1 获取锁，setnx
        //得到一个uuid 值，作为锁的值
        String uuid = UUID.randomUUID().toString();
        Boolean lock =
                redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);

        //2 获取锁成功、就执行相应业务
        if(lock) {
            Long decrement = redisTemplate.opsForValue()
                    .decrement(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
            // 此时当前商品已经没有库存
            if (decrement.intValue() < 0) {
                // v4.0 内存标记, 此时当前商品, 没有库存
                entryStockMap.put(goodsId, true);

                // 恢复库存数为 0
                redisTemplate.opsForValue().increment(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
                redisTemplate.execute(redisScript, Arrays.asList("lock"), uuid);
                model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
                return "secKillFail";
            }

            redisTemplate.execute(redisScript, Arrays.asList("lock"), uuid);
        } else {
            //3 获取锁失败、返回信息, 这次抢购失败,请继续抢购
            model.addAttribute("errmsg", RespBeanEnum.SEC_KILL_RETRY.getMessage());
            return "secKillFail";//错误页面
        }

        // 抢购, 向mq 发送秒杀商品请求，实现异步秒杀请求
        // 这里发送秒杀消息后，马上返回临时结果,比如排队中,因为不知道是否成功, 客户端需要轮询
        // errmsg 为排队中.... , 暂时返回"secKillFail" 页面
        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
        mqSenderMessage.sendSeckillMessage(JSONUtil.toJsonStr(seckillMessage));
        model.addAttribute("errmsg", "排队中.....");
        model.addAttribute("goodsId", goodsId);

        return "secKillFail";
    }

//    /**
//     * 处理秒杀商品下单v5.0
//     * redis预减库存 + 内存标记(减少到redis中 预减库存 次数) + rabbitmq 消息队列
//     * @param model
//     * @param user
//     * @param goodsId
//     * @return
//     */
//    @WebLog
//    @RequestMapping(value = "/doSeckill")
//    public String doSeckill(Model model, User user, Long goodsId) {
//        if (Objects.isNull(user)) {// 用户没有登录
//            return "login";
//        }
//
//        //将user放入model中
//        model.addAttribute("user",user);
//
//        //获取到goodsVo
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        //判断库存
//        if (goodsVo.getStockCount() < 1) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//
//        //判断用户是否复购[限购]
//        //v2.0 改成到redis中查询
//        ValueOperations opsForValue = redisTemplate.opsForValue();
//        SeckillOrder seckillOrder = (SeckillOrder)opsForValue
//                .get("order:" + user.getId() + ":" + goodsVo.getId());
//
//        if (!Objects.isNull(seckillOrder)) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
//            return "secKillFail";
//        }
//
//        // v4.0 对map进行判断[内存标记], 如果商品在redis中，已经标记为没有库存, 不再进行redis 预减库存
//        if (entryStockMap.get(goodsId)) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//
//        // v3.0增加 预减库存机制,如果在redis中预减库存, 发现商品已经没有了, 直接返回
//        // 达到减少执行 seckill 方法, 防止线程堆积, 优化高并发秒杀
//        // decrement 方法具有原子性
//        // 注意: 不能将这段代码放到, 判断用户复购前面, 可能会导致库存剩余,
//        // 库存预减 => 判断复购，如果恰好当前用户 已经购买, 直接返回信息, redis库存 白减1
//
//        Long decrement = redisTemplate.opsForValue()
//                .decrement(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
//        // 此时当前商品已经没有库存
//        if (decrement.intValue() < 0) {
//            // v4.0 内存标记, 此时当前商品, 没有库存
//            entryStockMap.put(goodsId, true);
//
//            // 恢复库存数为 0
//            redisTemplate.opsForValue().increment(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//
//        // 抢购, 向mq 发送秒杀商品请求，实现异步秒杀请求
//        // 这里发送秒杀消息后，马上返回临时结果,比如排队中,因为不知道是否成功, 客户端需要轮询
//        // errmsg 为排队中.... , 暂时返回"secKillFail" 页面
//        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
//        mqSenderMessage.sendSeckillMessage(JSONUtil.toJsonStr(seckillMessage));
//        model.addAttribute("errmsg", "排队中.....");
//        model.addAttribute("goodsId", goodsId);
//
//        return "secKillFail";
//    }


//    /**
//     * 处理秒杀商品下单v4.0-redis预减库存 + 内存标记(减少到redis中 预减库存 次数)
//     *
//     * @param model
//     * @param user
//     * @param goodsId
//     * @return
//     */
//    @WebLog
//    @RequestMapping(value = "/doSeckill")
//    public String doSeckill(Model model, User user, Long goodsId) {
//        //v1.0 版本,后续高并发时还要进行优化
//        if (Objects.isNull(user)) {//用户没有登录
//            return "login";
//        }
//
//        //将user放入model中
//        model.addAttribute("user",user);
//
//        //获取到goodsVo
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        //判断库存
//        if (goodsVo.getStockCount() < 1) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//
//        //判断用户是否复购[限购]
//        //v2.0 改成到redis中查询
//        ValueOperations opsForValue = redisTemplate.opsForValue();
//        SeckillOrder seckillOrder = (SeckillOrder)opsForValue
//                .get("order:" + user.getId() + ":" + goodsVo.getId());
//
//        if (!Objects.isNull(seckillOrder)) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
//            return "secKillFail";
//        }
//
//        // v4.0 对map进行判断[内存标记], 如果商品在redis中，已经标记为没有库存, 不再进行redis 预减库存
//        if (entryStockMap.get(goodsId)) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//
//        // v3.0增加 预减库存机制,如果在redis中预减库存, 发现商品已经没有了, 直接返回
//        // 达到减少执行 seckill 方法, 防止线程堆积, 优化高并发秒杀
//        // decrement 方法具有原子性
//        // 注意: 不能将这段代码放到, 判断用户复购前面, 可能会导致库存剩余,
//        // 库存预减 => 判断复购，如果恰好当前用户 已经购买, 直接返回信息, redis库存 白减1
//
//        Long decrement = redisTemplate.opsForValue()
//                .decrement(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
//        // 此时当前商品已经没有库存
//        if (decrement.intValue() < 0) {
//            // v4.0 内存标记, 此时当前商品, 没有库存
//            entryStockMap.put(goodsId, true);
//
//            // 恢复库存数为 0
//            redisTemplate.opsForValue().increment(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//
//        //进行秒杀购买
//        Order order = orderService.seckill(user, goodsVo);
//        if (Objects.isNull(order)) {//购买失败
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//        //成功,并携带到订单详情页
//        model.addAttribute("order",order);
//        model.addAttribute("goods",goodsVo);
//        //v1.0结束
//
//        return "orderDetail";
//    }


//    /**
//     * 处理秒杀商品下单v3.0-redis预减库存操作，减少到数据库查询次数
//     *
//     * @param model
//     * @param user
//     * @param goodsId
//     * @return
//     */
//    @RequestMapping(value = "/doSeckill")
//    public String doSeckill(Model model, User user, Long goodsId) {
//        //v1.0 版本,后续高并发时还要进行优化
//        if (Objects.isNull(user)) {//用户没有登录
//            return "login";
//        }
//
//        //将user放入model中
//        model.addAttribute("user",user);
//
//        //获取到goodsVo
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        //判断库存
//        if (goodsVo.getStockCount() < 1) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//
//        //判断用户是否复购[限购]
//        //v2.0 改成到redis中查询
//        ValueOperations opsForValue = redisTemplate.opsForValue();
//        SeckillOrder seckillOrder = (SeckillOrder)opsForValue
//                .get("order:" + user.getId() + ":" + goodsVo.getId());
//
//        if (!Objects.isNull(seckillOrder)) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
//            return "secKillFail";
//        }
//
//        // v3.0增加 预减库存机制,如果在redis中预减库存, 发现商品已经没有了, 直接返回
//        // 达到减少执行 seckill 方法, 防止线程堆积, 优化高并发秒杀
//        // decrement 方法具有原子性
//        // 注意: 不能将这段代码放到, 判断用户复购前面, 可能会导致库存剩余,
//        // 库存预减 => 判断复购，如果恰好当前用户 已经购买, 直接返回信息, redis库存 白减1
//
//        Long decrement = redisTemplate.opsForValue()
//                .decrement(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
//        // 此时商品已经没有库存
//        if (decrement.intValue() < 0) {
//            // 恢复库存数为 0
//            redisTemplate.opsForValue().increment(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsId);
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//
//        //进行秒杀购买
//        Order order = orderService.seckill(user, goodsVo);
//        if (Objects.isNull(order)) {//购买失败
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//        //成功,并携带到订单详情页
//        model.addAttribute("order",order);
//        model.addAttribute("goods",goodsVo);
//        //v1.0结束
//
//        return "orderDetail";
//    }

//    /**
//     * 处理秒杀商品下单v1.0/v2.0(升级为到redis中，查询秒杀订单信息)[逐渐优化]
//     *
//     * @param model
//     * @param user
//     * @param goodsId
//     * @return
//     */
//    @RequestMapping(value = "/doSeckill")
//    public String doSeckill(Model model, User user, Long goodsId) {
//        //v1.0 版本,后续高并发时还要进行优化
//        if (Objects.isNull(user)) {//用户没有登录
//            return "login";
//        }
//
//        //将user放入model中
//        model.addAttribute("user",user);
//
//        //获取到goodsVo
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        //判断库存
//        if (goodsVo.getStockCount() < 1) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//
//        //判断用户是否复购[限购]
////        SeckillOrder seckillOrder = seckillOrderService
////                .getOne(new LambdaQueryWrapper<SeckillOrder>()
////                        .eq(SeckillOrder::getGoodsId, goodsId)
////                        .eq(SeckillOrder::getUserId, user.getId()));
//        //v2.0 改成到redis中查询
//        ValueOperations opsForValue = redisTemplate.opsForValue();
//        SeckillOrder seckillOrder = (SeckillOrder)opsForValue
//                .get("order:" + user.getId() + ":" + goodsVo.getId());
//
//        if (!Objects.isNull(seckillOrder)) {
//            //错误信息
//            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
//            return "secKillFail";
//        }
//
//        //进行秒杀购买
//        Order order = orderService.seckill(user, goodsVo);
//        if (Objects.isNull(order)) {//购买失败
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
//            return "secKillFail";
//        }
//        //成功,并携带到订单详情页
//        model.addAttribute("order",order);
//        model.addAttribute("goods",goodsVo);
//        //v1.0结束
//
//        return "orderDetail";
//    }

    /**
     * @description: 实现redis预减库存机制 (将所有秒杀商品库存数，加载到redis中)
     * 该方法，在类的所有属性都初始化后，自动执行
     * @author: xujiabing
     * @date: 2024-12-09 21:00
     * @param:
     * @return: void
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("============开始查询秒杀商品库存=========");
        // 查询所有秒杀商品
        List<GoodsVo> list = goodsService.findGoodsVo();
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        // 遍历,获取秒杀商品库存,存入redis
        list.forEach(goodsVo -> {
            redisTemplate
                    .opsForValue()
                    .set(RedisCacheConstant.SECKILL_GOODS_STOCK + goodsVo.getId()
                            , goodsVo.getStockCount());

            // 初始化内存标记map, goodsId : false 有库存, true 没有库存了
            entryStockMap.put(goodsVo.getId(), false);
        });


        log.info("============结束查询秒杀商品库存=========");
    }
}
